package com.ecloud.support.generator.custom;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStreamWriter;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Properties;
import java.util.Set;

import org.apache.velocity.Template;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.Velocity;
import org.apache.velocity.app.VelocityEngine;

import com.baomidou.mybatisplus.core.toolkit.StringPool;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.generator.config.ConstVal;
import com.baomidou.mybatisplus.generator.config.builder.ConfigBuilder;
import com.baomidou.mybatisplus.generator.config.po.TableInfo;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
import com.baomidou.mybatisplus.generator.engine.AbstractTemplateEngine;
import com.ecloud.support.generator.model.dto.TableConfigDto;

/**
 * 自定义默认模板引擎
 *
 * @author LiuGangQiang Create in 2021/06/07
 */
public class DefaultTemplateEngine extends AbstractTemplateEngine {
    private VelocityEngine velocityEngine;
    private TableConfigDto dto;
    private CustomerTemplateConfig config;

    public DefaultTemplateEngine(TableConfigDto dto, CustomerTemplateConfig config) {
        this.dto = dto;
        this.config = config;
    }

    @Override
    public DefaultTemplateEngine init(ConfigBuilder configBuilder) {
        if (null == velocityEngine) {
            Properties p = new Properties();
            p.setProperty("resource.loader.file.class", ConstVal.VM_LOAD_PATH_VALUE);
            p.setProperty(Velocity.FILE_RESOURCE_LOADER_PATH, StringPool.EMPTY);
            p.setProperty(Velocity.ENCODING_DEFAULT, ConstVal.UTF8);
            p.setProperty(Velocity.INPUT_ENCODING, ConstVal.UTF8);
            p.setProperty("resource.loader.file.unicode", StringPool.TRUE);
            velocityEngine = new VelocityEngine(p);
        }
        return this;
    }

    @Override
    public void writer(Map<String, Object> objectMap, String templatePath, File outputFile) throws Exception {
        Template template = velocityEngine.getTemplate(templatePath, ConstVal.UTF8);
        try (FileOutputStream fos = new FileOutputStream(outputFile);
                OutputStreamWriter ow = new OutputStreamWriter(fos, ConstVal.UTF8);
                BufferedWriter writer = new BufferedWriter(ow)) {
            template.merge(new VelocityContext(objectMap), writer);
        }
    }

    @Override
    public String templateFilePath(String filePath) {
        final String dotVm = ".vm";
        return filePath.endsWith(dotVm) ? filePath : filePath + dotVm;
    }

    @Override
    public Map<String, Object> getObjectMap(ConfigBuilder config, TableInfo tableInfo) {
        Map<String, Object> objectMap = super.getObjectMap(config, tableInfo);
        /* 注入自定义配置信息 */
        objectMap.put(CustomerConstVal.DTO_NAME, CustomerConfigUtils.initDto(tableInfo, dto));
        objectMap.put(CustomerConstVal.VO_NAME, CustomerConfigUtils.initVo(tableInfo, dto));
        objectMap.put(CustomerConstVal.CONVERT_NAME, CustomerConfigUtils.initConvert(tableInfo, dto));
        return objectMap;
    }

    @Override
    public AbstractTemplateEngine batchOutput() {
        try {
            ConfigBuilder config = this.getConfigBuilder();
            List<TableInfo> tableInfoList = config.getTableInfoList();
            tableInfoList.forEach(tableInfo -> {
                Map<String, Object> objectMap = this.getObjectMap(config, tableInfo);
                Optional.ofNullable(config.getInjectionConfig()).ifPresent(t -> t.beforeOutputFile(tableInfo, objectMap));
                outputDto(tableInfo, objectMap);
                outputVo(tableInfo, objectMap);
                outputConvert(tableInfo, objectMap);
            });
        } catch (Exception e) {
            throw new RuntimeException("无法创建文件，请检查配置信息！", e);
        }
        return super.batchOutput();
    }

    protected void outputDto(TableInfo tableInfo, Map<String, Object> objectMap) {
        Set<String> prefix = new HashSet<>(Arrays.asList(dto.getPrefix()));
        String dtoName = NamingStrategy.capitalFirst(NamingStrategy.removePrefixAndCamel(tableInfo.getName(), prefix)) + CustomerConstVal.DTO_SUFFIX;
        String dtoPath = config.getDtoPath();
        if (StringUtils.isNotBlank(dtoName) && StringUtils.isNotBlank(dtoPath)) {
            String dtoFile = String.format((dtoPath + File.separator + "%s" + suffixJavaOrKt()), dtoName);
            outputFile(new File(dtoFile), objectMap, config.getDto());
        }
    }

    protected void outputVo(TableInfo tableInfo, Map<String, Object> objectMap) {
        Set<String> prefix = new HashSet<>(Arrays.asList(dto.getPrefix()));
        String voName = NamingStrategy.capitalFirst(NamingStrategy.removePrefixAndCamel(tableInfo.getName(), prefix)) + CustomerConstVal.VO_SUFFIX;
        String voPath = config.getVoPath();
        if (StringUtils.isNotBlank(voName) && StringUtils.isNotBlank(voPath)) {
            String voFile = String.format((voPath + File.separator + "%s" + suffixJavaOrKt()), voName);
            outputFile(new File(voFile), objectMap, config.getVo());
        }
    }

    protected void outputConvert(TableInfo tableInfo, Map<String, Object> objectMap) {
        Set<String> prefix = new HashSet<>(Arrays.asList(dto.getPrefix()));
        String convertName = NamingStrategy.capitalFirst(NamingStrategy.removePrefixAndCamel(tableInfo.getName(), prefix)) + CustomerConstVal.CONVERT_SUFFIX;
        String convertPath = config.getConvertPath();
        if (StringUtils.isNotBlank(convertName) && StringUtils.isNotBlank(convertPath)) {
            String convertFile = String.format((convertPath + File.separator + "%s" + suffixJavaOrKt()), convertName);
            outputFile(new File(convertFile), objectMap, config.getConvert());
        }
    }
}
